package authentication

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"github.com/denisbrodbeck/machineid"
	"os"
	"time"
)

type AppName string

type ApplicationAuthentication struct {
	processorID    string
	version        int8
	privateKeyPath string
	appName        AppName
}

func NewApplicationAuthentication(versionMajor int8, privateKeyPath string, appName AppName) (*ApplicationAuthentication, error) {
	appAuth := &ApplicationAuthentication{
		privateKeyPath: privateKeyPath,
		version:        versionMajor,
		appName:        appName,
	}
	err := appAuth.getProcessorID()
	if err != nil {
		return nil, err
	}

	return appAuth, nil
}

func (a *ApplicationAuthentication) CheckCode(licensePath string) error {
	activeCode, err := os.ReadFile(licensePath)
	if err != nil {
		return err
	}

	privateKeyFile, err := os.ReadFile(a.privateKeyPath)
	if err != nil {
		return err
	}

	block, _ := pem.Decode(privateKeyFile)
	if block == nil {
		return errors.New("解码私钥出错")
	}

	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return err
	}

	decryptedData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, activeCode, nil)
	if err != nil {
		return err
	}

	var res struct {
		ProcessorID string `json:"processorID"`
		Version     int8   `json:"version"`
		TimeStarted int64  `json:"timeStarted"`
		ExpireTime  int64  `json:"expireTime"`
		AppName     string `json:"appName"`
	}
	err = json.Unmarshal(decryptedData, &res)
	if err != nil {
		return err
	}

	if res.AppName != string(a.appName) {
		return errors.New("非该app的授权码")
	}

	if res.Version != a.version {
		return errors.New("当前版本非授权版本")
	}
	if (res.TimeStarted > 0 && res.TimeStarted > time.Now().Unix()) || (res.ExpireTime > 0 && res.ExpireTime < time.Now().Unix()) {
		return errors.New("当前授权码不在有效期内")
	}

	if res.ProcessorID != a.processorID {
		return errors.New("当前授权码与当前设备不匹配")
	}

	return nil
}

func (a *ApplicationAuthentication) getProcessorID() error {
	id, err := machineid.ProtectedID("")
	if err != nil {
		return err
	}
	a.processorID = id
	return nil
}
